package com.opencee.cloud.uaa.config;

import com.opencee.cloud.tools.api.IToolsUserAgentApi;
import com.opencee.cloud.uaa.filter.CaptchaLoginAuthenticationFilter;
import com.opencee.cloud.uaa.filter.GiteeLoginAuthenticationFilter;
import com.opencee.cloud.uaa.filter.QrCodeLoginAuthenticationFilter;
import com.opencee.cloud.uaa.filter.SmsCodeLoginAuthenticationFilter;
import com.opencee.cloud.uaa.handler.CustomAuthenticationEntryPoint;
import com.opencee.cloud.uaa.handler.CustomAuthenticationFailureHandler;
import com.opencee.cloud.uaa.handler.CustomAuthenticationSuccessHandler;
import com.opencee.cloud.uaa.handler.CustomLogoutSuccessHandler;
import com.opencee.cloud.uaa.provider.CaptchaAuthenticationProvider;
import com.opencee.cloud.uaa.provider.GiteeAuthenticationProvider;
import com.opencee.cloud.uaa.provider.QrCodeAuthenticationProvider;
import com.opencee.cloud.uaa.provider.SmsAuthenticationProvider;
import com.opencee.cloud.uaa.provider.service.GiteeUserDetailService;
import com.opencee.cloud.uaa.provider.service.QrCodeUserDetailService;
import com.opencee.cloud.uaa.provider.service.SmsCodeUserDetailService;
import com.opencee.cloud.uaa.provider.service.UsernameUserDetailService;
import com.opencee.common.exception.DefaultAccessDeniedHandler;
import com.opencee.common.utils.RedisTemplateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Web服务配置类
 *
 * @author liuyadu
 */
@Slf4j
@Order(100)
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    public WebSecurityConfiguration() {
        log.info("当前Web安全配置");
    }

    @Autowired
    private UsernameUserDetailService usernameUserDetailService;
    @Autowired
    private SmsCodeUserDetailService smsCodeUserDetailService;
    @Autowired
    private QrCodeUserDetailService qrcodeUserDetailService;
    @Autowired
    private GiteeUserDetailService giteeUserDetailService;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private RedisTemplateUtil redisTemplateUtil;
    @Autowired
    private UaaProperties authProperties;
    @Autowired
    private IToolsUserAgentApi toolsUserAgentApi;

    /**
     * 验证码登录
     *
     * @return
     */
    public CaptchaLoginAuthenticationFilter captchaLoginAuthenticationFilter() {
        CaptchaLoginAuthenticationFilter filter = new CaptchaLoginAuthenticationFilter();
        try {
            filter.setAuthenticationFailureHandler(this.authenticationFailureHandler());
            filter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return filter;
    }

    /**
     * 手机验证码登陆过滤器
     *
     * @return
     */
    public SmsCodeLoginAuthenticationFilter smsCodeLoginAuthenticationFilter() {
        SmsCodeLoginAuthenticationFilter filter = new SmsCodeLoginAuthenticationFilter();
        try {
            filter.setProperties(authProperties);
            filter.setAuthenticationFailureHandler(this.authenticationFailureHandler());
            filter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return filter;
    }

    /**
     * 二维码登录过滤器
     *
     * @return
     */
    public QrCodeLoginAuthenticationFilter qrCodeLoginAuthenticationFilter() {
        QrCodeLoginAuthenticationFilter filter = new QrCodeLoginAuthenticationFilter();
        try {
            filter.setProperties(authProperties);
            filter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler("/login/qrcode/error"));
            filter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return filter;
    }

    /**
     * gitee第三方登录
     *
     * @return
     */
    public GiteeLoginAuthenticationFilter giteeLoginAuthenticationFilter() {
        GiteeLoginAuthenticationFilter filter = new GiteeLoginAuthenticationFilter();
        try {
            filter.setProperties(authProperties);
            filter.setAuthenticationFailureHandler(this.authenticationFailureHandler());
            filter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return filter;
    }


    AuthenticationFailureHandler authenticationFailureHandler() {
        return new CustomAuthenticationFailureHandler("/login?error");
    }

    AuthenticationSuccessHandler authenticationSuccessHandler() {
        return new CustomAuthenticationSuccessHandler();
    }

    public LogoutHandler cookieClearingLogoutHandler() {
        return new CookieClearingLogoutHandler("token", "remember-me");
    }

    public CustomLogoutSuccessHandler logoutSuccessHandler() {
        return new CustomLogoutSuccessHandler();
    }

    public CaptchaAuthenticationProvider captchaAuthenticationProvider() {
        CaptchaAuthenticationProvider provider = new CaptchaAuthenticationProvider();
        // 设置userDetailsService
        provider.setUserDetailsService(usernameUserDetailService);
        provider.setRedisTemplateUtil(redisTemplateUtil);
        provider.setPasswordEncoder(passwordEncoder);
        // 禁止隐藏用户未找到异常
        provider.setHideUserNotFoundExceptions(false);
        provider.setToolsUserAgentApi(toolsUserAgentApi);
        return provider;
    }

    public SmsAuthenticationProvider smsAuthenticationProvider() {
        SmsAuthenticationProvider provider = new SmsAuthenticationProvider();
        // 设置userDetailsService
        provider.setUserDetailsService(smsCodeUserDetailService);
        // 禁止隐藏用户未找到异常
        provider.setHideUserNotFoundExceptions(false);
        provider.setToolsUserAgentApi(toolsUserAgentApi);
        return provider;
    }

    public QrCodeAuthenticationProvider qrAuthenticationProvider() {
        QrCodeAuthenticationProvider provider = new QrCodeAuthenticationProvider();
        // 设置userDetailsService
        provider.setUserDetailsService(qrcodeUserDetailService);
        // 禁止隐藏用户未找到异常
        provider.setHideUserNotFoundExceptions(false);
        provider.setToolsUserAgentApi(toolsUserAgentApi);
        return provider;
    }

    public GiteeAuthenticationProvider giteeAuthenticationProvider() {
        GiteeAuthenticationProvider provider = new GiteeAuthenticationProvider();
        // 设置userDetailsService
        provider.setUserDetailsService(giteeUserDetailService);
        provider.setRedisTemplateUtil(redisTemplateUtil);
        // 禁止隐藏用户未找到异常
        provider.setHideUserNotFoundExceptions(false);
        provider.setToolsUserAgentApi(toolsUserAgentApi);
        return provider;
    }


    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(usernameUserDetailService)
                .passwordEncoder(passwordEncoder);
        auth.authenticationProvider(captchaAuthenticationProvider());
        auth.authenticationProvider(smsAuthenticationProvider());
        auth.authenticationProvider(qrAuthenticationProvider());
        auth.authenticationProvider(giteeAuthenticationProvider());
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // 使用自定义登录页进入,防止参数丢失
        CustomAuthenticationEntryPoint entryPoint = new CustomAuthenticationEntryPoint("/login");
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and().authorizeRequests()
                .antMatchers("/login/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin().loginPage("/login").permitAll().successHandler(this.authenticationSuccessHandler())
                .failureHandler(this.authenticationFailureHandler())
                // 登出页
                .and().logout()
                .logoutUrl("/logout")
                .addLogoutHandler(cookieClearingLogoutHandler())
                .logoutSuccessHandler(logoutSuccessHandler())
                // 异常处理
                .and().exceptionHandling()
                .authenticationEntryPoint(entryPoint)
                // 自定义Token异常信息,用于token校验失败返回信息
                // 授权异常处理
                .accessDeniedHandler(new DefaultAccessDeniedHandler())
                .and()
                .csrf().disable()
                // 禁用httpBasic
                .httpBasic().disable();
        // 自定义认证过滤器
        http.apply(new CustomAuthenticationConfigurer(
                Arrays.asList(
                        captchaLoginAuthenticationFilter(),
                        smsCodeLoginAuthenticationFilter(),
                        qrCodeLoginAuthenticationFilter(),
                        giteeLoginAuthenticationFilter())
        ));
    }

    private static class CustomAuthenticationConfigurer
            extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

        private List<AbstractAuthenticationProcessingFilter> filterList;

        CustomAuthenticationConfigurer(
                List<AbstractAuthenticationProcessingFilter> filterList) {
            this.filterList = filterList;
            if (this.filterList == null) {
                this.filterList = new ArrayList<>();
            }
        }

        @Override
        public void configure(HttpSecurity builder) throws Exception {
            ApplicationContext context = builder.getSharedObject(ApplicationContext.class);
            AuthenticationManager authenticationManager = context.getBean(AuthenticationManager.class);
            SessionAuthenticationStrategy sessionAuthenticationStrategy = builder.getSharedObject(SessionAuthenticationStrategy.class);
            this.filterList.forEach(t -> {
                t.setAuthenticationManager(authenticationManager);
                t.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
                builder.addFilterBefore(t, UsernamePasswordAuthenticationFilter.class);
            });


        }

    }

}
